///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef __Mesh_H__
#define __Mesh_H__

#include "OgrePrerequisites.h"

#include "OgreResource.h"
#include "OgreVertexIndexData.h"
#include "math/axisAlignedBox.h"
#include "OgreVertexBoneAssignment.h"
#include "OgreIteratorWrappers.h"
#include "OgreHardwareVertexBuffer.h"
#include "OgreSkeleton.h"
#include "OgreAnimation.h"
#include "OgreAnimationTrack.h"
#include "OgrePose.h"
#include "OgreDataStream.h"


namespace Ogre {


	
	
	

    struct MeshLodUsage;
    class LodStrategy;

    class _OgreExport Mesh: public Resource, public AnimationContainer
    {
        friend class SubMesh;
        friend class MeshSerializerImpl;
        friend class MeshSerializerImpl_v1_4;
        friend class MeshSerializerImpl_v1_2;
        friend class MeshSerializerImpl_v1_1;

    public:
		typedef vector<Real>::type LodValueList;
        typedef vector<MeshLodUsage>::type MeshLodUsageList;
        /// Multimap of vertex bone assignments (orders by vertex index)
		typedef multimap<size_t, VertexBoneAssignment>::type VertexBoneAssignmentList;
        typedef MapIterator<VertexBoneAssignmentList> BoneAssignmentIterator;
        typedef vector<SubMesh*>::type SubMeshList;
        typedef vector<unsigned short>::type IndexMap;

    protected:
        
        SubMeshList mSubMeshList;
	
        
        void organiseTangentsBuffer(VertexData *vertexData, 
			VertexElementSemantic targetSemantic, unsigned short index, 
			unsigned short sourceTexCoordSet);

    public:
		
		typedef HashMap<String, ushort> SubMeshNameMap ;

		
    protected:

        DataStreamPtr mFreshFromDisk;

		SubMeshNameMap mSubMeshNameMap ;

        /// Local bounding box volume
        AxisAlignedBox mAABB;
		/// Local bounding sphere radius (centered on object)
		Real mBoundRadius;

        /// Optional linked skeleton
        String mSkeletonName;
        SkeletonPtr mSkeleton;

       
        VertexBoneAssignmentList mBoneAssignments;

        /// Flag indicating that bone assignments need to be recompiled
        bool mBoneAssignmentsOutOfDate;

        
        void buildIndexMap(const VertexBoneAssignmentList& boneAssignments,
            IndexMap& boneIndexToBlendIndexMap, IndexMap& blendIndexToBoneIndexMap);
        
        void compileBoneAssignments(const VertexBoneAssignmentList& boneAssignments,
            unsigned short numBlendWeightsPerVertex, 
            IndexMap& blendIndexToBoneIndexMap,
            VertexData* targetVertexData);

        const LodStrategy *mLodStrategy;
		bool mIsLodManual;
		ushort mNumLods;
		MeshLodUsageList mMeshLodUsageList;

		HardwareBuffer::Usage mVertexBufferUsage;
		HardwareBuffer::Usage mIndexBufferUsage;
		bool mVertexBufferShadowBuffer;
		bool mIndexBufferShadowBuffer;


        bool mPreparedForShadowVolumes;
        bool mEdgeListsBuilt;
        bool mAutoBuildEdgeLists;

		/// Storage of morph animations, lookup by name
		typedef map<String, Animation*>::type AnimationList;
		AnimationList mAnimationsList;
		/// The vertex animation type associated with the shared vertex data
		mutable VertexAnimationType mSharedVertexDataAnimationType;
		/// Whether vertex animation includes normals
		mutable bool mSharedVertexDataAnimationIncludesNormals;
		/// Do we need to scan animations for animation types?
		mutable bool mAnimationTypesDirty;

		/// List of available poses for shared and dedicated geometryPoseList
		PoseList mPoseList;
		mutable bool mPosesIncludeNormals;


        
        void prepareImpl(void);
        
        void unprepareImpl(void);
        /// @copydoc Resource::loadImpl
        void loadImpl(void);
		/// @copydoc Resource::postLoadImpl
		void postLoadImpl(void);
        /// @copydoc Resource::unloadImpl
        void unloadImpl(void);
		/// @copydoc Resource::calculateSize
		size_t calculateSize(void) const;



    public:
        
        Mesh(ResourceManager* creator, const String& name, ResourceHandle handle,
            const String& group, bool isManual = false, ManualResourceLoader* loader = 0);
        ~Mesh();

		// NB All methods below are non-virtual since they will be
        // called in the rendering loop - speed is of the essence.

        
        SubMesh* createSubMesh(void);

		
		SubMesh* createSubMesh(const String& name);
		
		
		void nameSubMesh(const String& name, ushort index);

		
		void unnameSubMesh(const String& name);
		
		
		ushort _getSubMeshIndex(const String& name) const;

        
        unsigned short getNumSubMeshes(void) const;

        
        SubMesh* getSubMesh(unsigned short index) const;

		
		SubMesh* getSubMesh(const String& name) const ;
		
		
		void destroySubMesh(unsigned short index);

		
		void destroySubMesh(const String& name);
		
        typedef VectorIterator<SubMeshList> SubMeshIterator;
        /// Gets an iterator over the available submeshes
        SubMeshIterator getSubMeshIterator(void)
        { return SubMeshIterator(mSubMeshList.begin(), mSubMeshList.end()); }
      
        
        VertexData *sharedVertexData;

        
        IndexMap sharedBlendIndexToBoneIndexMap;

        
        MeshPtr clone(const String& newName, const String& newGroup = StringUtil::BLANK);

        
        const AxisAlignedBox& getBounds(void) const;

		
		Real getBoundingSphereRadius(void) const;

        
        void _setBounds(const AxisAlignedBox& bounds, bool pad = true);

        
        void _setBoundingSphereRadius(Real radius);

        
        void setSkeletonName(const String& skelName);

        
        bool hasSkeleton(void) const;

		
		bool hasVertexAnimation(void) const;
		
		
        const SkeletonPtr& getSkeleton(void) const;

        
        const String& getSkeletonName(void) const;
        
        void _initAnimationState(AnimationStateSet* animSet);

		
		void _refreshAnimationState(AnimationStateSet* animSet);
        
        void addBoneAssignment(const VertexBoneAssignment& vertBoneAssign);

        
        void clearBoneAssignments(void);

        
        void _notifySkeleton(SkeletonPtr& pSkel);


        
        BoneAssignmentIterator getBoneAssignmentIterator(void);

		
		const VertexBoneAssignmentList& getBoneAssignments() const { return mBoneAssignments; }


		
		ushort getNumLodLevels(void) const;
		
		const MeshLodUsage& getLodLevel(ushort index) const;
		
		void createManualLodLevel(Real value, const String& meshName, const String& groupName = Ogre::String());

		
		void updateManualLodLevel(ushort index, const String& meshName);

		
		ushort getLodIndex(Real value) const;

		
		bool isLodManual(void) const { return mIsLodManual; }

		
		void _setLodInfo(unsigned short numLevels, bool isManual);
		
		void _setLodUsage(unsigned short level, MeshLodUsage& usage);
		
		void _setSubMeshLodFaceList(unsigned short subIdx, unsigned short level, IndexData* facedata);

        
        void removeLodLevels(void);

		
		void setVertexBufferPolicy(HardwareBuffer::Usage usage, bool shadowBuffer = false);
		
		void setIndexBufferPolicy(HardwareBuffer::Usage usage, bool shadowBuffer = false);
        
        HardwareBuffer::Usage getVertexBufferUsage(void) const { return mVertexBufferUsage; }
        
        HardwareBuffer::Usage getIndexBufferUsage(void) const { return mIndexBufferUsage; }
        
        bool isVertexBufferShadowed(void) const { return mVertexBufferShadowBuffer; }
        
        bool isIndexBufferShadowed(void) const { return mIndexBufferShadowBuffer; }
       

        
        unsigned short _rationaliseBoneAssignments(size_t vertexCount, VertexBoneAssignmentList& assignments);

        
        void _compileBoneAssignments(void);

        
        void _updateCompiledBoneAssignments(void);

        
        void buildTangentVectors(VertexElementSemantic targetSemantic = VES_TANGENT,
			unsigned short sourceTexCoordSet = 0, unsigned short index = 0, 
			bool splitMirrored = false, bool splitRotated = false, bool storeParityInW = false);

        
        bool suggestTangentVectorBuildParams(VertexElementSemantic targetSemantic,
			unsigned short& outSourceCoordSet, unsigned short& outIndex);

        
        void buildEdgeList(void);
        
        void freeEdgeList(void);

        
        void prepareForShadowVolume(void);

        
        EdgeData* getEdgeList(unsigned short lodIndex = 0);

        
        const EdgeData* getEdgeList(unsigned short lodIndex = 0) const;

        
        bool isPreparedForShadowVolumes(void) const { return mPreparedForShadowVolumes; }

		
		bool isEdgeListBuilt(void) const { return mEdgeListsBuilt; }

        
        static void prepareMatricesForVertexBlend(const Matrix4** blendMatrices,
            const Matrix4* boneMatrices, const IndexMap& indexMap);

        
        static void softwareVertexBlend(const VertexData* sourceVertexData, 
            const VertexData* targetVertexData,
            const Matrix4* const* blendMatrices, size_t numMatrices,
            bool blendNormals);

        
        static void softwareVertexMorph(Real t, 
            const HardwareVertexBufferSharedPtr& b1, 
			const HardwareVertexBufferSharedPtr& b2, 
			VertexData* targetVertexData);

        
		static void softwareVertexPoseBlend(Real weight, 
			const map<size_t, Vector3>::type& vertexOffsetMap,
			const map<size_t, Vector3>::type& normalsMap,
			VertexData* targetVertexData);
        
        const SubMeshNameMap& getSubMeshNameMap(void) const { return mSubMeshNameMap; }

        
        void setAutoBuildEdgeLists(bool autobuild) { mAutoBuildEdgeLists = autobuild; }
        
        bool getAutoBuildEdgeLists(void) const { return mAutoBuildEdgeLists; }

		
		virtual VertexAnimationType getSharedVertexDataAnimationType(void) const;

		/// Returns whether animation on shared vertex data includes normals
		bool getSharedVertexDataAnimationIncludesNormals() const { return mSharedVertexDataAnimationIncludesNormals; }

		
        virtual Animation* createAnimation(const String& name, Real length);

        
        virtual Animation* getAnimation(const String& name) const;

		
		virtual Animation* _getAnimationImpl(const String& name) const;

		
		virtual bool hasAnimation(const String& name) const;

        
        virtual void removeAnimation(const String& name);

		
		virtual unsigned short getNumAnimations(void) const;

		
		virtual Animation* getAnimation(unsigned short index) const;

		
		virtual void removeAllAnimations(void);
		
		VertexData* getVertexDataByTrackHandle(unsigned short handle);
        
  		void updateMaterialForAllSubMeshes(void);

		
		void _determineAnimationTypes(void) const;
		
		bool _getAnimationTypesDirty(void) const { return mAnimationTypesDirty; }

		
		Pose* createPose(ushort target, const String& name = StringUtil::BLANK);
		
		size_t getPoseCount(void) const { return mPoseList.size(); }
		
		Pose* getPose(ushort index);
		
		Pose* getPose(const String& name);
		
		void removePose(ushort index);
		
		void removePose(const String& name);
		
		void removeAllPoses(void);

		typedef VectorIterator<PoseList> PoseIterator;
		typedef ConstVectorIterator<PoseList> ConstPoseIterator;

		
		PoseIterator getPoseIterator(void);
		
		ConstPoseIterator getPoseIterator(void) const;
		
		const PoseList& getPoseList(void) const;

        
        const LodStrategy *getLodStrategy() const;
        
        void setLodStrategy(LodStrategy *lodStrategy);

    };

    
    class _OgreExport MeshPtr : public SharedPtr<Mesh> 
    {
    public:
        MeshPtr() : SharedPtr<Mesh>() {}
        explicit MeshPtr(Mesh* rep) : SharedPtr<Mesh>(rep) {}
        MeshPtr(const MeshPtr& r) : SharedPtr<Mesh>(r) {} 
        MeshPtr(const ResourcePtr& r);
        /// Operator used to convert a ResourcePtr to a MeshPtr
        MeshPtr& operator=(const ResourcePtr& r);
    protected:
        /// Override destroy since we need to delete Mesh after fully defined
        void destroy(void);
    };

	
	struct MeshLodUsage
	{
        
        Real userValue;

		
		Real value;
		
		/// Only relevant if mIsLodManual is true, the name of the alternative mesh to use
		String manualName;
		/// Only relevant if mIsLodManual is true, the name of the group of the alternative mesh
		String manualGroup;
		/// Hard link to mesh to avoid looking up each time
		mutable MeshPtr manualMesh;
        /// Edge list for this LOD level (may be derived from manual mesh)
        mutable EdgeData* edgeData;

		MeshLodUsage() : userValue(0.0), value(0.0), edgeData(0) {}
	};

	
	


} // namespace

#endif
